package Wangcache

import (
	"Wangcache/singleflight"
	"fmt"
	"log"
	"sync"
)

//这个文件的作用就是负责与外部交互，控制缓存存储和获取的主流程
//这里解决了一个问题，当所需要的数据没有在缓存中，那么我们就设计一个回调函数，在缓存不存在时调用这个函数得到源数据

//定期接口Getter
type Getter interface {
	Get(key string)([]byte,error)
}
//定义函数类型GetterFunc，并实现Getter接口的Get方法
type GetterFunc func(key string)([]byte,error)

//该函数类型实现了这个接口，称之为接口型函数，方便使用者在调用时既能够传入函数作为参数，也能够传入实现了该接口的结构体作为参数
//就相当于有两种身份
func (f GetterFunc)Get(key string)([]byte,error){
	return f(key)
}
//接下来是最核心的数据结构Group的定义
//一个Group可以认为是一个缓存的命名空间，每个Group拥有一个唯一的名称name。比如可以创建三个Group，缓存学生的成绩命名为scores，缓存学生信息的命名为info，缓存学生课程的命名为coyrses
type Group struct {
	name string
	getter Getter//缓存未命中时获取源数据的回调(callback)
	mainCache cache//即一开始实现的并发缓存
	peers PeerPicker
	loader *singleflight.Group
}
var(
	mu sync.RWMutex
	groups=make(map[string]*Group)
)
//NewGroup 创建一个group，并将group存储在全局变量groups中
func NewGroup(name string,cacheBytes int64,getter Getter)*Group{
	if getter==nil{
		panic("nil Getter")
	}
	mu.Lock()
	defer mu.Unlock()
	g:=&Group{
		name: name,
		getter: getter,
		mainCache: cache{cacheBytes: cacheBytes},
		loader: &singleflight.Group{},
	}
	groups[name]=g
	return g
}
//GetGroup用来获得特定名称的Group，这里只用了读锁，因为不涉及任何冲突变量的写操作
func GetGroup(name string)*Group{
	mu.RLock()
	g:=groups[name]
	mu.RUnlock()
	return g
}

//Get方法
func (g *Group)Get(key string)(ByteView,error){
	if key==""{
		return ByteView{},fmt.Errorf("key is required")
	}
	if v,ok:=g.mainCache.get(key);ok{
		log.Println("[WangCache]hit")
		return v,nil
	}
	return g.load(key)
}
func (g *Group)load(key string)(value ByteView,err error){
	viewi,err:=g.loader.Do(key, func() (interface{}, error) {
		//在这里修改load方法，使用pickpeer()方法选择节点，若非本机节点，则调用getFromPeer()从远程获取。若是本机节点或失败，则回退到getLocally()
		if g.peers!=nil{
			if peer,ok:=g.peers.PickPeer(key);ok{
				if value,err=g.getFromPeer(peer,key);err==nil{
					return value,nil
				}
				log.Println("[WangCache]Failed to get from peer",err)
			}
		}
		return g.getLocally(key)
	})
	if err==nil{
		return viewi.(ByteView),nil
	}
	return
}
func (g *Group)getLocally(key string)(ByteView,error){
	bytes,err:=g.getter.Get(key)
	if err!=nil{
		return ByteView{},err
	}
	value:=ByteView{b:cloneBytes(bytes)}
	g.populateCache(key,value)
	return value,nil
}
func (g *Group)populateCache(key string,value ByteView){
	g.mainCache.add(key,value)
}

//新增RegisterPeers()方法，将实现了PeerPicker接口的HTTPPool注入到Group中
func (g *Group)RegisterPeers(peers PeerPicker){
	if g.peers!=nil{
		panic("RegisterPeerPicker called more than once")
	}
	g.peers=peers
}

//新增getFromPeer()方法，使用实现了PeerGetter接口的httpGetter从访问远程节点获取缓存值
func (g *Group)getFromPeer(peer PeerGetter,key string)(ByteView,error){
	bytes,err:=peer.Get(g.name,key)
	if err!=nil{
		return ByteView{},err
	}
	return ByteView{b:bytes},nil
}





